// Filename: MemexWebHelp.js
// Created by: Alistair Christie, Memex Technology Ltd
// Last amended: December 2009

// No support will be given for modifications to this script
// or for any reuse of this script.


// Include secondary JS file(s) in this file (e.g. for testing purpses):
// (Note: breaking up the word 'script' is necessary to avoid problems with some IIS servers)
//document.write("<scr"+"ipt language='javascript' src='Resources/JavaScript/issueReporting.js'></scr"+"ipt>"); 




var collapseAllLinkText = "collapse all";
var expandAllLinkText = "expand all";
var expanderTooltip = 'Click to show/hide information';
var expandAllTooltip = 'Click to show all additional information';
var collapseAllTooltip = 'Click to hide all additional information';
var sIcon_BackToTop = "Resources/Images/back-to-top.gif";	
var expandingSectionBackgroundColour = "#F1F3FA";


$(document).ready(function(){

	// Transform glossPopup spans to popup anchors			
	$('span.glossPopup').each( function(){
		var customPopupWidth = "";
		var widthSpecified = $(this).attr('width');
		if (widthSpecified) {
			customPopupWidth = ""
		}
		var anchorgloss = $('<a class="glossPopup" title="Click for a pop-up definition" onclick="UAshowGlossaryPopup(\'' 
			+ $(this).attr('MadCap:targetName') + '\',event, \'' + $(this).attr('width') + '\'); return false" href="javascript:void(0)"></a>').	
		append($(this).contents());
		$(this).replaceWith(anchorgloss);
	});	
	
	//Append a link to the CSS file
	var newCSSlink=document.createElement('link');
	newCSSlink.setAttribute("href","Resources/StyleSheets/MCstylesOverrider.css");
	newCSSlink.setAttribute("rel","stylesheet");
	newCSSlink.setAttribute("type", "text/css");
	headElement = document.getElementsByTagName("head")[0];
	headElement.appendChild(newCSSlink);	

	//Append raw CSS, inside an IE-specific comment, to allow browsers <IE8 to see the body:
    $('head').append("<!--[if lt IE 8]><style>body{display:block;}</style><![endif]-->");	
	
	//Add a class to the body element:
	$('body').addClass('topicBody');
	
	// ==================================================================================
	// Transform expand/collapse sections
	// ----------------------------------------------------------------------------------	
	// Make the background of a li with the class expandCollapse white 
	// (it's blue by default, purely as a visual indicator within Flare). 
	// We're going to add a div, which will have a coloured background, but because it's got a gap above it 
	// we need to make the li white otherwise the blue will show between the first line in the li & the start of the div. 
	// The if clause kicks in for expanding blocks within expanding blocks, in which case we want the background colour
	// to be the same as the background colour of the parent div, rather than white.

	// Find each li.expandCollapse and for each one: 
	// - set the background colour
	// - add a tooltip
	// - wrap the 2nd & subsequent child elements within that li in a div (the block of text that we'll expand): 
	$('li.expandCollapse').each(
		function() {
			if ( $(this).parents('div.expandableBlock').length ) {
				$(this).css('background-color',expandingSectionBackgroundColour);
			}
			else {
				$(this).css('background-color','white');
			}
			$(this).attr('title', expanderTooltip);
			$(this).children(':not(:first-child)').wrapAll('<div class="expandableBlock"></div>'); 
		}
	);		
	// Use jQuery's hover() function to add/remove an additional class to the li 
	// when the cursor passes over the li. 
	// The new class changes the bullet image and the colour of the text of the first element within the li. 
	$('li.expandCollapse').hover(
		function() {   //On hover over:
			if ($(this).hasClass('currentlyExpanded') ) {
				$(this).removeClass('expandedSectionBulletCold');
				$(this).addClass('expandedSectionBulletHot');  
			}
			else {
				$(this).addClass('collapsedSectionBulletHot');  
			}
		}, 
		function() {   //On hover out:
			if ($(this).hasClass('currentlyExpanded') ) {
				$(this).removeClass('expandedSectionBulletHot'); 
				$(this).addClass('expandedSectionBulletCold');
			}
			else {
				$(this).removeClass('collapsedSectionBulletHot'); 
			}
		}
	);	
	// Do the same for the first element within the li, so that passing out of this element 
	// switches off the highlighting - e.g. when you move the cursor down from the "hotspot" text and over the div 
	// of an already expanded drop-down block: 		
	$('li.expandCollapse').children(':first-child').hover(
		function() {  //On hover over:
			if ($(this).parent().hasClass('currentlyExpanded') ) {
				$(this).parent().removeClass('expandedSectionBulletCold');
				$(this).parent().addClass('expandedSectionBulletHot');  
			}
			else {
				$(this).parent().addClass('collapsedSectionBulletHot');  
			}
		}, 
		function() {  //On hover out:
			if ($(this).parent().hasClass('currentlyExpanded') ) {
				$(this).parent().removeClass('expandedSectionBulletHot'); 
				$(this).parent().addClass('expandedSectionBulletCold');
			}
			else {
				$(this).parent().removeClass('collapsedSectionBulletHot'); 
			}
		}
	);		
	// We're changing the classes of the li whenever the cursor passes over it. 
	// However, if the cursor passes upwards into the div of an already expanded section,  
	// this would cause the li's .hover() event handler to be triggered and the li would "go hot". 
	// To prevent this happening, we do nothing when we hover over any of the 2nd or subsequent elements within the li. 		
	// But we use "return(false)" to prevent the hover event bubbling upwards from the child element into the parent li 		
	// which would trigger the .hover() event handler on the li element. 
	// We also remove the title attribute of the li to prevent the tooltip being displayed when you're over the div
	// and then we add it back in again when the cursor leaves the div.
	$('li.expandCollapse').children(':not(:first-child)').hover(
		function() {   //On hover over:
			$(this).parent().removeAttr('title');
			return(false);
		}, 
		function() {   //On hover out:
			$(this).parent().attr('title', expanderTooltip);		
		}
	);			


	// The expanding div is hidden by default (see style for div.expandableBlock in the CSS file).
	// We'll now use jQuery slideDown or slideUp on the div when the parent li element is clicked.
	$('li.expandCollapse').click(
		function() {
			var expandingBlock = $(this).children(':nth-child(2)');
			var dsp = expandingBlock.css('display');
			if (dsp == "none") {
				expandingBlock.slideDown();
				$(this).removeClass('collapsedSectionBulletHot');
				$(this).addClass('currentlyExpanded');
				$(this).addClass('expandedSectionBulletHot');
				
				var numExpandSectionsExpanded = $('.currentlyExpanded').length;
				if (numExpandSectionsExpanded == numExpandSectionsTotal) {
					$('a.expandall').text(collapseAllLinkText);
					$('a.expandall').attr('title', collapseAllTooltip);
				}
			}
			else {
				expandingBlock.slideUp();
				$(this).removeClass('currentlyExpanded');
				$(this).removeClass('expandedSectionBulletHot');				
				$(this).addClass('collapsedSectionBulletHot');
								
				var numExpandSectionsExpanded = $('.currentlyExpanded').length;
				if (numExpandSectionsExpanded == (numExpandSectionsTotal-1)) {
					$('a.expandall').text(expandAllLinkText);
					$('a.expandall').attr('title', expandAllTooltip);
				}
			}
		}
	);	
	// Prevent a click on the div itself causing a click event, which would bubble up to the li
	// and cause it to slide up:
	$('div.expandableBlock').click(
		function() {
			return(false);
		}
	);
	// Similar to previous, prevent a click on a hyperlink within the div from bubbling up
	// and causing the div to slide up:
	$('div.expandableBlock a').click(
		function(event){ 
			event.stopPropagation(); 
		}
	);
	
	$('a.jumpWithinPage').hover(
		function() {
			if (!$(this).attr('title')) {
				$(this).attr('title', 'Move to this section');			
			}
		}
	);
	$('a.jumpWithinPage').click(
		function() {
			jumpWithinPage($(this).attr('href'));
		}
	);
	
	var expandallElements = $('a.expandall');
	expandallElements.attr('title', expandAllTooltip);
	expandallElements.click(
		function() {
			var expanderListItems = $('li.expandCollapse');
			if ($('.currentlyExpanded').length != $('li.expandCollapse').length) {
				expanderListItems.children(':nth-child(2)').slideDown();
				expanderListItems.removeClass('collapsedSectionBulletHot');
				expanderListItems.addClass('currentlyExpanded');
				expanderListItems.addClass('expandedSectionBulletCold');
				expandallElements.text(collapseAllLinkText);
				expandallElements.attr('title', collapseAllTooltip);
				return(false);
			}
			else {
				expanderListItems.children(':nth-child(2)').slideUp('fast');
				expanderListItems.removeClass('currentlyExpanded');
				expanderListItems.removeClass('expandedSectionBulletCold');				
				expandallElements.text(expandAllLinkText);
				expandallElements.attr('title', expandAllTooltip);
				return(false);
			}	
		}
	);	


	// Add the "Back to top" icon, top & bottom right of window:
	$('<a id="backtotoplinktop" style="position: fixed; bottom: 5px; right: 5px; display: none" href="#" title="Back to top" onclick="window.scrollTo(0,0); return false"><img src="' +
		sIcon_BackToTop + '"></a>').appendTo("body");
	$('<a id="backtotoplinkbottom" style="position: fixed; top: 5px; right: 5px; display: none" href="#" title="Back to top" onclick="window.scrollTo(0,0); return false"><img src="' +
		sIcon_BackToTop + '"></a>').appendTo("body");
	// Show/hide the icon when you scroll down/up:
	var backToTopIconsVisible = false;
	$(window).scroll(function () { 
				//$("#tempInfo").text(document.documentElement.scrollTop); // FOR TESTING PURPOSES
		var scrollTop = document.documentElement.scrollTop;
		if ( scrollTop > 30 && !backToTopIconsVisible) {
			$("#backtotoplinktop").fadeIn("slow");
			$("#backtotoplinkbottom").fadeIn("slow");
			backToTopIconsVisible = true;
		}
		else if ( scrollTop == 0 ) {
			$("#backtotoplinktop").fadeOut("fast");
			$("#backtotoplinkbottom").fadeOut("fast");
			backToTopIconsVisible = false;
		}
    });

	
	var numExpandSectionsTotal  = $('li.expandCollapse').length;  //Store the number of expander sections

	// ----------------------------------------------------------------------------------
	// End of expand/collapse transformation
	// ==================================================================================
	
	
	//If the URL contains an anchor hash, try & expand the related expanding section:
	if (numExpandSectionsTotal > 0 && window.location.hash) {
		var currentHash=window.location.hash;
		currentHash=currentHash.replace(/^./,"");
		requestedListItem = $("a[name='"+currentHash+"']").parents("li.expandCollapse:first");
		requestedListItem.click();
		requestedListItem.removeClass('expandedSectionBulletHot');	
		requestedListItem.addClass('currentlyExpanded');
		requestedListItem.addClass('expandedSectionBulletCold');
	}
		
	$("body").css('display','block');  // After everything has been transformed, show the page.
});
// END OF $(document).ready 
// ////////////////////////////////////////////////////////////////////////////////////////////////


function showPageLinkInfo() {

	if(!$("#infoPanel").length) {
		var topicpath = document.location.pathname;
		var filename = topicpath.substr(topicpath.lastIndexOf('/') + 1);
		
		var locationPath = String(top.document.location);
		if ((locationPath.lastIndexOf('.html')) == -1) locationPath = locationPath + "index.html";
		
		var hashIndex = locationPath.lastIndexOf('#');
		if (hashIndex > -1) locationPath = locationPath.substring(0, hashIndex);
		var pathMinusExtension = locationPath.substring(0, locationPath.lastIndexOf('.'));
				
		var documentPath = pathMinusExtension + "_CSH.html";

		var linkref = documentPath + "#" + filename;
		var linkrefForMail = documentPath + "%23" + filename;
		var topicTitleForEmail = "";
		var bookmarkName = "";
		if ($('h1').length) {
			var h1Text = $('h1').text();
			topicTitleForEmail = "Help topic: %22" + h1Text + "%22%0D%0ALink: ";
			bookmarkName = "title=\"Memex Help - " + h1Text + "\" ";
		}
		
		var infoPanel = '<div id="infoPanel" style="background-color:#F8F8F8;display:none;padding:0;margin:0 -2em 0 -2em;border-bottom:solid;border-color:lightgrey">' +
			'<div style="padding:2em; text-align:center">' +

			'<p style="font-weight:bold">Copy, email or bookmark a link to this page</p>' +			
			'<p><span id="clipboard" style="font-family:\'Courier New\',courier,monospace">' + linkref + '</span></p>' +			
			'<p><span id="copyTrigger" style="text-decoration:underline;color:blue">Copy link to clipboard</span><br/>' +			
			'<a href="mailto:?subject=Memex Online Help&body=%0D%0A' + topicTitleForEmail + linkrefForMail + '">Email page link</a><br/>' +	
			
			'<a id="add2favorites" href="' + linkref + '" ' + bookmarkName + '>Add to browser Favorites/Bookmarks</a></p>' +	

			'</div></div>';
			
		$('body').prepend(infoPanel);  // Add the infoPanel div (initially hidden)
		$(' #add2favorites').jFav();   // Modify the bookmark link to add bookmarking functionality (from http://www.dummwiam.com/jFav)

		var copy2clip = new ZeroClipboard.Client();                      
		copy2clip.setText("");
		copy2clip.setHandCursor(true);
		copy2clip.setCSSEffects(true); 
		copy2clip.addEventListener("mouseDown", function(client) {
			copy2clip.setText( $("span#clipboard").text() );
			$("span#copyTrigger").css("font-weight","bold");
			var t=setTimeout("$('span#copyTrigger').css('font-weight','normal')",100);
		} );
		
		$(window).resize(function(){
			copy2clip.reposition();
		});

	}

	$("#infoPanel").slideToggle("slow");
	copy2clip.glue("copyTrigger");

}


function locationHashChanged() {
	//This function gets triggered by calling a named anchor on another part of the current page
	//(e.g. when you click an entry in the index for elsewhere on the same page)
	//Close all expandable sections:
	$('li.currentlyExpanded').children(':nth-child(2)').hide();
	$('li.expandedSectionBulletCold').removeClass('expandedSectionBulletCold');				
	$('li.currentlyExpanded').removeClass('currentlyExpanded');
	//Change the "expand/collapse all" link(s):
	var expandallElements = $('a.expandall');
	expandallElements.text(expandAllLinkText);
	expandallElements.attr('title', expandAllTooltip);
	var URLhash = window.location.hash;
	//Reset the anchor to the top of the page
	//(it may have moved up if expanded sections above collapsed):
	document.location.href=window.location.hash;
	//Remove the "#" from the hash:
	var currentHash = URLhash.replace(/^./,"");
	//Find the list item containing this anchor:
	requestedListItem = $("a[name='"+currentHash+"']").parents("li.expandCollapse:first");
	//Click the list item:
	requestedListItem.click();
	requestedListItem.removeClass('expandedSectionBulletHot');	
	requestedListItem.addClass('currentlyExpanded');
	requestedListItem.addClass('expandedSectionBulletCold');
}
window.onhashchange = locationHashChanged;
//Note: the onhashchange event is only detected in IE8 & FF3.6 and above.


function jumpWithinPage(anchorName) {
	//This function gets triggered by clicking a link with class="jumpWithinPage"
  	//containing an href to somewhere on the same page.
	//Close all expandable sections:
	$('li.currentlyExpanded').children(':nth-child(2)').hide();
	$('li.expandedSectionBulletCold').removeClass('expandedSectionBulletCold');				
	$('li.currentlyExpanded').removeClass('currentlyExpanded');
	//Change the "expand/collapse all" link(s):
	var expandallElements = $('a.expandall');
	expandallElements.text(expandAllLinkText);
	expandallElements.attr('title', expandAllTooltip);
	//Remove the "#" from the anchor name:
	anchorName = anchorName.replace(/^./,"");
	//Find the list item containing this anchor:
	requestedListItem = $("a[name='"+anchorName+"']").parents("li.expandCollapse:first");
	//Click the list item:
	requestedListItem.click();
	requestedListItem.removeClass('expandedSectionBulletHot');	
	requestedListItem.addClass('currentlyExpanded');
	requestedListItem.addClass('expandedSectionBulletCold');
}


function UAshowGlossaryPopup(UAglossaryID,e,UApopupWidth) {  
  //NOTE: As of release 2.5 (Feb 2007) Madcap Flare rewrites
  //XML files included in project when you build the output, 
  //replacing its existing content with Madcap-ised XML.
  //One of the problems with this is that it removes the entries
  //from their CDATA blocks, which causes problems if you want to
  //include HTML tags within the content.
  //To avoid this, give the XML file a different
  //file name extension - e.g. call it glossary.dat
  var sGlossaryFile = "Resources/Glossary/glossary.dat";
  sBrowserType = fDetectBrowserType(); 
  nDefaultPopupWidth = 350;
  sIcon_PopupsCloseButtonCold = "Resources/Images/popups-close-button-cold.gif";
  
  document.onmousedown = fCheckWhetherTitleBarClicked;
  document.onmouseup = Function("bTitleBarClicked=false");

  if (typeof(UAbrowserType) == "undefined") 
  {  
    UAbrowserType = UAdetectBrowserType();
  }
  
  //Test whether insertAdjacentElement is available:
  if (typeof HTMLElement!="undefined" && ! HTMLElement.prototype.insertAdjacentElement)
  {
    //If not (as in Firefox), create this & similar methods:
    UAcreateInsertAdjacentMethods();
  }  
  //Check whether a popup div for the clicked word has already been created:
  var UArequestedPopupElement = $("#UA"+UAglossaryID);
  if (!UArequestedPopupElement.html()) 
  {  
    //If not, create it now.
    if ( !UApopupWidth || UApopupWidth=="undefined" )   //No width was specified for the popup
    {
      UApopupWidth = nDefaultPopupWidth;
    }
    oXMLGlossaryDocument = UAloadXML(sGlossaryFile);
    UAretrieveGlossaryEntryFromXML(UAglossaryID,oXMLGlossaryDocument);
    UAcreatePopupDiv(UAglossaryID,UApopupWidth);
  }
  UArevealPopup(UAglossaryID,e);

} // End of UAshowGlossaryPopup function



function UAloadXML(sXMLfile)
{
  var mozillaTypeBrowser = (typeof document.implementation != 'undefined') &&
    (typeof document.implementation.createDocument != 'undefined');
  var ie =  (typeof window.ActiveXObject != 'undefined');
  var xmlDoc;

  if (mozillaTypeBrowser)
  {
    xmlDoc = document.implementation.createDocument("", "", null);
    xmlDoc.async = false;
    xmlDoc = fixXmlMimeType(sXMLfile);
  }
  else if (ie)
  {
    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async=false;
    xmlDoc.load(sXMLfile);
  }
  //If neither method is supported, do nothing.
  else return false;

  // If it's all good, return the object.
  if (typeof xmlDoc != "undefined") return xmlDoc;
  else return false;
}


function fixXmlMimeType(sXMLfile)
{
  //Note: This function gets used for Mozilla-type browsers, which don't
  //recognise XML in an XML file that has a .dat file name extension.
  //We need to use the .dat file name extension to prevent Flare from
  //rewriting our XML and breaking it.
  oXMLhttp = new XMLHttpRequest();
  oXMLhttp.overrideMimeType("text/xml");
  oXMLhttp.open("GET", sXMLfile, false);
  oXMLhttp.send(null);
  return oXMLhttp.responseXML;
}


function UAretrieveGlossaryEntryFromXML(UAglossaryID,oXMLGlossaryDocument)
{
    var foundID = false;

    //Mozilla-type browsers (e.g. Firefox) behave differently to IE,
    //so determine whether the browser being used is a Mozilla-type browser 
    //and store this for later use:
    var mozillaTypeBrowser = (typeof document.implementation != 'undefined') && 
      (typeof document.implementation.createDocument != 'undefined');

    //Get the root element (in this case "glossary"):
    var oRootElement = oXMLGlossaryDocument.documentElement;      

    //For each element in the childNodes array (i.e. each child node in the file):
    for ( i = 0; i < oRootElement.childNodes.length; i++ )   
    { 
      var level1Node = oRootElement.childNodes.item(i);      //Get the child node

      if ( level1Node.nodeName == "glossentry" )     //Only parse the "glossentry" nodes
      {
        //Check whether the current "glossentry" node is the one selected by the user:
        if ( level1Node.getAttribute('id') == UAglossaryID )
        {
            foundID = true;

            //Within the selected "glossentry" node ...
            //... get the "glossterm" element:
            var glosstermElements = level1Node.getElementsByTagName("glossterm");
            //... get the "glossdef" element:
            var glossdefElements = level1Node.getElementsByTagName("glossdef");

            if ( mozillaTypeBrowser )
            {
                //The .firstChild.nodeValue method doesn't work in Mozilla-type browsers,
                //because it ignores everything in CDATA sections, 
                //whereas we need to retrieve this data.
                //So we use XMLSerializer (in the uaXMLtoString function below)
                //to turn the entire "glossterm" and "glossdef" elements into strings:
                sGlossTerm = uaXMLtoString(glosstermElements[0]);
                sGlossDef = uaXMLtoString(glossdefElements[0]);

                //We now have the "glossterm" and "glossdef" elements
                //and we use regular expressions to:
                //a) remove "<glossterm><![CDATA[" and "]]></glossterm>" from 
                //the start & end of the string:
                sGlossTerm = sGlossTerm.replace(/[\s\r\n]*<\s*glossterm\s*>[\s\r\n]*<!\s*\[\s*CDATA\s*\[[\s\r\n]*/gi, "");
                sGlossTerm = sGlossTerm.replace(/[\s\r\n]*\]\s*\]\s*>[\s\r\n]*<\s*\/\s*glossterm>[\s\r\n]*/gi, "");
                //b) remove "<glossdef><![CDATA[" and "]]></glossdef>" from the start 
                //and end of the string:
                sGlossDef  = sGlossDef.replace(/[\s\r\n]*<\s*glossdef\s*>[\s\r\n]*<!\s*\[\s*CDATA\s*\[[\s\r\n]*/gi, "");
                sGlossDef  = sGlossDef.replace(/[\s\r\n]*\]\s*\]\s*>[\s\r\n]*<\s*\/\s*glossdef>[\s\r\n]*/gi, "");

                //alert("glossterm:\n\n" + sGlossTerm);  //for debugging
                //alert("glossdef:\n\n" + sGlossDef);    //for debugging
            }
            else        //in Internet Explorer:
            {
                //Get the glossary term and definition text:
                sGlossTerm = glosstermElements[0].firstChild.nodeValue;
                sGlossDef = glossdefElements[0].firstChild.nodeValue;

                //Use regular expressions to strip the whitespace at 
                //the start & end of the strings:
                sGlossTerm = sGlossTerm.replace(/^[\s\r\n]*/, "");
                sGlossTerm = sGlossTerm.replace(/[\s\r\n]*$/, "");
                sGlossDef  = sGlossDef.replace(/^[\s\r\n]*/, "");
                sGlossDef  = sGlossDef.replace(/[\s\r\n]*$/, "");

                //alert("glossterm:\n\n" + sGlossTerm);  //for debugging
                //alert("glossdef:\n\n" + sGlossDef);    //for debugging
            }
        }
      }
    }
    if (!foundID) 
	{
      sGlossTerm = "ERROR";
      sGlossDef = "No match was found for the link reference: " + 
                  "<strong>" + UAglossaryID + "</strong>.";
    }
	return true;
} // End of UAretrieveGlossaryEntryFromXML function



function UArevealPopup(UAglossaryID,e)
{
    // Note: the global variables XposOnClick and YposOnClick
    // are used to hold the X/Y coordinates of the mouse position.

    var aXYpositionArray = new Array();    
    aXYpositionArray = UAgetXYpos(e);

    XposOnClick = aXYpositionArray[0];  //Capture from-top position of click.
    YposOnClick = aXYpositionArray[1];  //Capture from-left position of click.
 
    var aArrayWindowWidthHeight = new Array();    
    aArrayWindowWidthHeight = UAgetWindowWidthHeight();
    var nWindowWidth = aArrayWindowWidthHeight[0];
    var nWindowHeight = aArrayWindowWidthHeight[1];
    var aArrayScrollAmounts = new Array();
    aArrayScrollAmounts = UAgetScrollAmounts();
    var nScrolledFromLeft = aArrayScrollAmounts[0];
    var nScrolledFromTop = aArrayScrollAmounts[1];
    var nPopupHeight = document.getElementById("UA"+UAglossaryID).offsetHeight;
    var nPopupWidth = document.getElementById("UA"+UAglossaryID).offsetWidth;

    if (UAbrowserType == "IE5DOM") nPopupHeight -= 20; //Tweak popup height for non-"standards-compliant" browser (e.g. Help Viewer)
    var nOffsetFromYclick = 10; //Pixels below the Y co-ord of the click point

	nPopupTop = YposOnClick + nScrolledFromTop + nOffsetFromYclick;
	//Note: nScrolledFromTop is either 0, or the amount in pixels you've scrolled down the page
	nPopupLeft = XposOnClick + nScrolledFromLeft -(nPopupWidth/2);
	//Note: nScrolledFromLeft is either 0, or the amount in pixels you've scrolled to the right

		// //For debugging: alert box giving info about window & popup dimensions
		 // alert(" X click point (XposOnClick): " + XposOnClick +
		 // "\n Y click point (YposOnClick): " + YposOnClick +
		 // "\n\n nWindowWidth: " + nWindowWidth +
		 // "\n nWindowHeight: " + nWindowHeight +
		 // "\n\n nPopupWidth (document.getElementById("+UAglossaryID+").offsetWidth): " + document.getElementById("UA"+UAglossaryID).offsetWidth +
		 // "\n nPopupLeft, left side of popup box: " + nPopupLeft +
		 // "\n Middle top of box (same as X unless scrolled right): " + eval(nPopupLeft+(nPopupWidth/2)) +
		 // "\n Right side of popup box: " + eval(nPopupLeft+nPopupWidth) +
		 // "\n nPopupTop, top of popup box: " + nPopupTop +
		 // "\n nPopupHeight, height of popup box: " + nPopupHeight +
		 // "\n\n document.documentElement.clientWidth: " + document.documentElement.clientWidth +
		 // "\n document.body.clientWidth: " + document.body.clientWidth +
		 // "\n\n document.documentElement.clientHeight: " + document.documentElement.clientHeight +
		 // "\n document.body.clientHeight: " + document.body.clientHeight +
		 // "\n nScrolledFromLeft: " + nScrolledFromLeft +
		 // "\n nScrolledFromTop: " + nScrolledFromTop
		 // );

    // //The next lot of 'if' clauses check that the popup isn't displayed partly off the screen:
    //First, make sure the co-ord of the right side isn't more than the width of the window
    if ( (nPopupLeft + nPopupWidth) > (nWindowWidth + nScrolledFromLeft) ) 
	{
        nPopupLeft = ( nWindowWidth - nPopupWidth - 10 ) + nScrolledFromLeft;
		if (UAbrowserType == "netscape") 
		{
			nPopupLeft -= 17;  //Allow for vertical scrollbar in Firefox 
		}		
    }	
    //Make sure the co-ord of the left side isn't further left than the left edge of the window
    if (nPopupLeft < nScrolledFromLeft) 
	{
        nPopupLeft = nScrolledFromLeft +2;
    }
    //Make sure the co-ord of the bottom of the popup isn't more than the height of the window
    //If it is, change the popup's co-ords so that it's displayed above the click point
    if ( (nPopupTop + nPopupHeight + 5) > nWindowHeight ) 
	{
        nPopupTop = (YposOnClick + nScrolledFromTop) - (nPopupHeight + 10);
        //Note: 10 is the offset, in pixels, between the click point
        //and the bottom of the popup		
    }
    //Make sure the co-ord of the top of the popup isn't above the top of the screen
    if ( (nPopupTop + nScrolledFromTop) < nScrolledFromTop ) 
	{
        nPopupTop = nScrolledFromTop + 2;
    }

	var glossPopId = "UA"+UAglossaryID;
	$("#" + glossPopId).css({top: nPopupTop, left: nPopupLeft});
	$("#" + glossPopId).css('visibility','visible');
    
	// Create a meta element within the head that has name="CurrentlyDisplayedPopup" content=glossPopId
	$('head').append("<meta name=\"CurrentlyDisplayedPopup\" content=\"" + glossPopId + "\" />");
}



function uaXMLtoString(node)
{
    try
    {
        var objXMLSerializer = new XMLSerializer;
        var strXML = objXMLSerializer.serializeToString(node);
        return strXML;
    }
    catch(e)
    {
        alert(e);
        return false;
    }
}



function UAcreatePopupDiv (UAglossaryID,nPopupWidth)
{
  //Create the HTML for the glosspopup:
  var sPopupDiv =
    '<div id="UA'+
    UAglossaryID
    +'" style="position:absolute;width:'+
    nPopupWidth
    +'px;left:0;top:0;visibility:hidden" class="dropshad"><table '
    +'class="glosspopupOuter innershadow"><tr><td width="100%" class="glosspopupOuter">'
    +'<table class="glosspopupTitle" ><tr><td id="'+
    UAglossaryID
    +'-titlebar" class="glosspopupTitle" style="cursor:move" ><layer onselectstart="return false;" width="100%">'+
    sGlossTerm
    +'</layer></td><td class="glosspopupTitle"><a href="#" id="popupCloseButton" '
    +'title="Close"><img src='+
    sIcon_PopupsCloseButtonCold
    +' alt="Close" /></a></td></tr></table><table class="glosspopupContents"><tr>'
    +'<td class="glosspopupContents" width="350px">'+
    sGlossDef
    +'</td></tr></table></td></tr></table></div>';

  //Add the div just before the </body> tag:
  document.body.insertAdjacentHTML("beforeEnd", sPopupDiv);  
}



function UAcreateInsertAdjacentMethods()
{
  // This function emulates insertAdjacentHTML(), insertAdjacentText()
  // and insertAdjacentElement() in browsers that do not support these methods
  // (e.g. Firefox).

  // Credit:
  // insertAdjacentHTML(), insertAdjacentText() and insertAdjacentElement()
  // for Netscape 6/Mozilla by Thor Larholm me@jscript.dk

	HTMLElement.prototype.insertAdjacentElement = function (where,parsedNode)
	{
		switch (where)
		{
		    case 'beforeBegin':
			    this.parentNode.insertBefore(parsedNode,this);
			    break;
		    case 'afterBegin':
			    this.insertBefore(parsedNode,this.firstChild);
			    break;
		    case 'beforeEnd':
			    this.appendChild(parsedNode);
			    break;
		    case 'afterEnd':
			    if (this.nextSibling) this.parentNode.insertBefore(parsedNode,this.nextSibling);
			    else this.parentNode.appendChild(parsedNode);
			    break;
		}
	}

	HTMLElement.prototype.insertAdjacentHTML = function (where,htmlStr)
	{
		var r = this.ownerDocument.createRange();
		r.setStartBefore(this);
		var parsedHTML = r.createContextualFragment(htmlStr);
		this.insertAdjacentElement(where,parsedHTML);
	}


	HTMLElement.prototype.insertAdjacentText = function (where,txtStr)
	{
		var parsedText = document.createTextNode(txtStr);
		this.insertAdjacentElement(where,parsedText);
	}
}


function UAdetectBrowserType() {
  var UAbrowserType="";
  if( typeof( window.innerWidth ) == 'number' ) {
    //Netscape compliant
    UAbrowserType="netscape";
  }
  else if( document.documentElement &&
      ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE6 in "standards compliant mode"
    UAbrowserType="IE6standardscompliantmode";
  }
  else {
    //Slipped through the net. Lets assume the browser is
    //IE4, IE5 or IE6 not in standards compliant mode
    UAbrowserType="IE5DOM";
  }
  return UAbrowserType;
}



function UAgetWindowWidthHeight() 
{   
  var nWindowWidth = 0, nWindowHeight = 0;
  if( UAbrowserType == 'netscape' ) {
    nWindowWidth = window.innerWidth;
    nWindowHeight = window.innerHeight;
  }
  else if( UAbrowserType == 'IE5DOM' ) {
    nWindowWidth = document.body.clientWidth;
    nWindowHeight = document.body.clientHeight + 8;
  }
  else if( UAbrowserType == 'IE6standardscompliantmode' ) {
    nWindowWidth = document.documentElement.clientWidth;
    nWindowHeight = document.documentElement.clientHeight;
  }
  return [ nWindowWidth, nWindowHeight ];
}


function fCheckWhetherTitleBarClicked(e)
{
	var clickedElement;      //The clicked element.
	var nTitleBarClicked;    //An indicator of whether the clicked element was the title bar.

	// Check whether a previously added meta element exists identifying the ID of a currently displayed glossary popup
	if ($("meta[name='CurrentlyDisplayedPopup']").length > 0) 
	{
		var sPopupCurrentlyDisplayed = $("meta[name='CurrentlyDisplayedPopup']").attr("content"); 
	}
	else {
		var sPopupCurrentlyDisplayed = "";
	}  
 
  if (sPopupCurrentlyDisplayed=="")
  {
    return;  //Don't go any further if no popup is currently displayed.
  }
  else
  {
    if (typeof event != "undefined" && event.srcElement)
    {
        clickedElement = event.srcElement;
		//alert("HTML of clicked object: \n" + clickedElement.outerHTML);
    }
    else
    {
        clickedElement = e.target;
    }
	//Check in case the clicked element has no ID
	//(this happens, in Mozilla, when the layer within the td of the 
	//title bar was clicked).        
	if (clickedElement.id == "")
	{
		clickedElement = clickedElement.parentNode; //Get the ID of the parent element 
									//(which may be the td, so we'll check its ID instead).
	}
	
    //If the click wasn't on an element (e.g. on a bit of screen below the 
    //last element on the page)
    //nTitleBarClicked is given the value -1
    //If the clicked element *is* defined, check whether its ID ends in "-titlebar",
    //if no match is found nTitleBarClicked also gets -1
    if (typeof clickedElement.id == "undefined")
    {
        nTitleBarClicked = -1;
    }
    else
    {
        nTitleBarClicked = clickedElement.id.search(/-titlebar$/);
    }
    if (nTitleBarClicked != -1)
    {       
      //if the clicked element ends in "-titlebar" (i.e. the mousedown event occurred 
      //in the title bar of the popup)
      //Extract the div name (i.e. the clicked bit prior to "-titlebar", 
      //e.g. if the id is "giraffe-titlebar" the div is "giraffe"
      sPopupDivName = "UA"+clickedElement.id.substring(0, nTitleBarClicked);
	  
	  //Get the co-ordinate of the top of the currently displayed popup box
	  var sPopupTopWhenClicked = $("#"+sPopupDivName).css("top");   //Get the CSS top value
	  var pxStartPoint = sPopupTopWhenClicked.search(/px/);    //find where "px" begins in the string value
	  sPopupTopWhenClicked = sPopupTopWhenClicked.substring(0, pxStartPoint);  //remove "px" from the value
	  nPopupTopWhenClicked = +sPopupTopWhenClicked;            //Create a global variable containing number from the string value.
	  //Do the same for the left co-ordinate:
	  var sPopupLeftWhenClicked = $("#"+sPopupDivName).css("left");
	  pxStartPoint = sPopupLeftWhenClicked.search(/px/);
	  sPopupLeftWhenClicked = sPopupLeftWhenClicked.substring(0, pxStartPoint);
	  nPopupLeftWhenClicked = +sPopupLeftWhenClicked;

      var aXYpositionArray = new Array();
      aXYpositionArray = UAgetXYpos(e);	  
      XposOnClick = aXYpositionArray[0];  //Capture from-top position of click.
      YposOnClick = aXYpositionArray[1];  //Capture from-left position of click.

      bTitleBarClicked=true;
		  //////TEST:
		  // alert("XposOnClick=" + XposOnClick +
		  //      "\nYposOnClick=" + YposOnClick +
		  //      "\npopupTopWhenClicked=" + nPopupTopWhenClicked +
		  //      "\npopupLeftWhenClicked=" + nPopupLeftWhenClicked
		  //      );
		  //////END OF TEST////////
		  document.onmousemove=fMovePopup;  //Call fMovePopup function.
    }
    else   //The click occurred outside the title bar of the popup:
    {      
      var closePopup = 1; //Popup will close unless this changes to 0.

      if (typeof clickedElement.id == "string")  //The click occurred within an element area
                                       // (i.e. not below the bottom of the HTML for the page)
      {
        if (clickedElement.className == "glosspopupContents") closePopup = 0;
        else if (clickedElement.parentNode && clickedElement.parentNode.className == "glosspopupContents") closePopup = 0;
        else if (clickedElement.parentNode.parentNode && clickedElement.parentNode.parentNode.className == "glosspopupContents") closePopup = 0;
      }
      if (closePopup)
      {
        UAcloseGlossaryPopup(sPopupCurrentlyDisplayed);  //Close the popup
      }
    }
  } //End of first, main else clause.
} //End of fCheckWhetherTitleBarClicked function.




function fMovePopup(e){
    if (!bTitleBarClicked)
    {
        return;   //Don't go any further unless the click occurred on the title bar of the popup.
    }
    else
    {
        var aXYpositionArray = new Array();
        aXYpositionArray = UAgetXYpos(e);
        XposOnMove = aXYpositionArray[0];  //Capture from-top position of mouse.
        YposOnMove = aXYpositionArray[1];  //Capture from-left position of mouse.

		// *** TEST - Replace the h1 heading with the current co-ordinates when you drag the glossaryp popup:
		// $('h1:first').html("#"+sPopupDivName + " | " + nPopupTopWhenClicked + " | " + nPopupLeftWhenClicked + " | " + XposOnMove + " | " + YposOnMove + " | " + XposOnClick + " | " + YposOnClick);  		

		$("#"+sPopupDivName).css("top",  nPopupTopWhenClicked + (YposOnMove - YposOnClick));
		$("#"+sPopupDivName).css("left",  nPopupLeftWhenClicked + (XposOnMove - XposOnClick));

        return false;
    }
}


function UAcloseGlossaryPopup(sPopupDivName)
{
  // Stop detecting these mouse events now that we're hiding the glossary popup:
  document.onMouseDown = null;
  document.onMouseUp = null;
  document.onMouseMove = null;

  //alert(document.getElementById('popupCloseButton').childNodes[0].src);
  
  document.getElementById('popupCloseButton').childNodes[0].src = sIcon_PopupsCloseButtonCold;
  
  //alert(document.getElementById('popupCloseButton').childNodes[0].src);
    
  document.getElementById('popupCloseButton').style.className="popupCloseButtonCold";  
    
  //Hide the popup:
  document.getElementById(sPopupDivName).style.visibility="hidden";

  //Note: There's an IE-only bug here that leaves the "X" button in it's active state
  //(i.e. the version of the button that's displayed onmouseover) when you close the 
  //popup by clicking "X". This means that, in IE, if you then redisplay that popup
  //the "X" button appears to have the focus.

  // Remove the meta element that was added to record the ID of the currently displayed glossary popup:
  $("meta[name='CurrentlyDisplayedPopup']").remove();
}


function fDetectBrowserType() {
  var sBrowserType="";
  if( typeof( window.innerWidth ) == 'number' ) {
    //Netscape compliant
    sBrowserType="netscape";
  }
  else if( document.documentElement &&
      ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE6 in "standards compliant mode"
    sBrowserType="IE6standardscompliantmode";
  }
  else {
    //Slipped through the net. Lets assume the browser is
    //IE4, IE5 or IE6 not in standards compliant mode
    sBrowserType="IE5DOM";
  }
  return sBrowserType;
}

/* NOT USED ANY MORE
function fGetXYpos(e)
{
    //This function returns the X (pixels from top of window) and Y (pixels from left)
    //values for the current mouse event.
    //The argument is always either 'event' (without the quotes)
    //or a variable (such as e) that holds the event.
    //The function returns an array of 2 elements: [ X, Y]

    var Xposition = 0, Yposition = 0;

    // Get the X/Y coords if the browser is IE:
    if (sBrowserType == "IE6standardscompliantmode" || sBrowserType == "IE5DOM")
    {
alert("mdm - 1556");	
        Xposition = event.clientX + document.body.scrollLeft;
        Yposition = event.clientY + document.body.scrollTop;
    }
    else  // Get the X/Y coords if the browser is non-IE:
    {
        Xposition = e.pageX;
        Yposition = e.pageY;
    }
	
	// $('h1:first').html(Xposition + " | " + Yposition);  // *** TEST 
    return [ Xposition, Yposition ];
}	
*/	

function UAgetXYpos(e)
{
    //This function returns the X (pixels from top of window) and Y (pixels from left)
    //values for the current mouse event.
    //The argument is always either 'event' (without the quotes)
    //or a variable (such as e) that holds the event.
    //The function returns an array of 2 elements: [ X, Y]
  
    var Xposition = 0, Yposition = 0;
	if (!e) var e = window.event;

    // Get the X/Y coords if the browser is IE:
    if (e.clientX || e.clientY)
    {
        Xposition = e.clientX;
        Yposition = e.clientY;
    }
    else if (e.pageX || e.pageY) // Get the X/Y coords if the browser is non-IE:
    {
        Xposition = e.pageX;
        Yposition = e.pageY;
    }
    return [ Xposition, Yposition ];
}

function UAgetScrollAmounts() 
{
  var nScrolledFromLeft = 0, nScrolledFromTop = 0;
  if( window.pageYOffset || window.pageXOffset ) {
    nScrolledFromTop = window.pageYOffset;
    nScrolledFromLeft = window.pageXOffset;
  }
  else if( document.documentElement.scrollTop || document.documentElement.scrollLeft ) {
    nScrolledFromTop = document.documentElement.scrollTop;
    nScrolledFromLeft = document.documentElement.scrollLeft;
  }
  else if( UAbrowserType == 'IE5DOM' ) {
    nScrolledFromTop = document.body.scrollTop -10;
    nScrolledFromLeft = document.body.scrollLeft +2;
  }
  return [ nScrolledFromLeft, nScrolledFromTop ];
}




/* ******************************************* */
/* ******* Third-party software ************** */
/* ******************************************* */

// jFav, JQuery plugin
// v 1.0 
// SAFARI & CHROME not Supported!
// Licensed under GPL licenses.
// Copyright (C) 2008 Nikos "DuMmWiaM" Kontis, info@dummwiam.com
// http://www.DuMmWiaM.com/EffectChain
// ----------------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ----------------------------------------------------------------------------

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(7($){$.8.9=7(d){g e=$.v({},$.8.9.k,d);g f=e.l;2.w(7(){5(!$(2).3(\'4\')||$(2).3(\'4\')=="#"){$(2).3(\'4\',j.x.4)}5(!$(2).3(\'6\')){$(2).3(\'6\',y.6)}5($.h.z){$(2).3(\'m\',$(2).3(\'4\')).3(\'4\',\'\').3(\'4\',\'A:B(0)\').3(\'C\',\'$.8.9.n(2,"\'+$(2).3(\'6\')+\'","\'+$(2).3(\'m\')+\'")\')}i{$(2).o(p)}});7 p(a){g b=$(2).3(\'4\');g c=$(2).3(\'6\');b=b.E("q:///","F://q/");5($.h.G){j.H.I(b,c)}i 5($.h.J){j.r.K(c,b,\'\')}i 5(L.M.N().O(\'P\')>-1){s(f)}i 5($.h.Q){s(f)}a.R()};t 2};$.8.9.k={l:\'S "T + D" U "V + D" W X, u Y 2 Z u 10 11.\'};$.8.9.n=7(e,a,b){e.12="r";e.6=a;e.4="";e.4=b;e.o();t 13}})(14);',62,67,'||this|attr|href|if|title|function|fn|jFav|||||||var|browser|else|window|defaults|msg|link|OperaBookmark|click|addToFavorites|file|sidebar|alert|return|to|extend|each|location|document|opera|javascript|void|onClick||replace|http|msie|external|AddFavorite|mozilla|addPanel|navigator|userAgent|toLowerCase|indexOf|chrome|safari|preventDefault|Press|Ctrl|or|CMD|for|MAC|add|page|your|bookmarks|rel|false|jQuery'.split('|'),0,{}))




/* ******************************************* */
/* ******* Third-party software ************** */
/* ******************************************* */

// Simple Set Clipboard System
// Author: Joseph Huckaby

var ZeroClipboard = {
	
	version: "1.0.5",
	clients: {}, // registered upload clients on page, indexed by id
	moviePath: 'ZeroClipboard.swf', // URL to movie
	nextId: 1, // ID of next movie
	
	$: function(thingy) {
		// simple DOM lookup utility function
		if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
		if (!thingy.addClass) {
			// extend element with a few useful methods
			thingy.hide = function() { this.style.display = 'none'; };
			thingy.show = function() { this.style.display = ''; };
			thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
			thingy.removeClass = function(name) {
				this.className = this.className.replace( new RegExp("(^|\\s+)" + name + "(\\s+|$)"), "").replace(/^\s+|\s+$/g, '');
			};
			thingy.hasClass = function(name) {
				return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
			};
		}
		return thingy;
	},
	
	setMoviePath: function(path) {
		// set path to ZeroClipboard.swf
		this.moviePath = path;
	},
	
	dispatch: function(id, eventName, args) {
		// receive event from flash movie, send to client		
		var client = this.clients[id];
		if (client) {
			client.receiveEvent(eventName, args);
		}
	},
	
	register: function(id, client) {
		// register new client to receive events
		this.clients[id] = client;
	},
	
	getDOMObjectPosition: function(obj, stopObj) {
		// get absolute coordinates for dom element
		var info = {
			left: 0, 
			top: 0, 
			width: obj.width ? obj.width : obj.offsetWidth, 
			height: obj.height ? obj.height : obj.offsetHeight
		};

		while (obj && (obj != stopObj)) {
			info.left += obj.offsetLeft;
			info.top += obj.offsetTop;
			obj = obj.offsetParent;
		}

		return info;
	},
	
	Client: function(elem) {
		// constructor for new simple upload client
		this.handlers = {};
		
		// unique ID
		this.id = ZeroClipboard.nextId++;
		this.movieId = 'ZeroClipboardMovie_' + this.id;
		
		// register client with singleton to receive flash events
		ZeroClipboard.register(this.id, this);
		
		// create movie
		if (elem) this.glue(elem);
	}
};

ZeroClipboard.Client.prototype = {
	
	id: 0, // unique ID for us
	ready: false, // whether movie is ready to receive events or not
	movie: null, // reference to movie object
	clipText: '', // text to copy to clipboard
	handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
	cssEffects: true, // enable CSS mouse effects on dom container
	handlers: null, // user event handlers
	
	glue: function(elem, appendElem, stylesToAdd) {
		// glue to DOM element
		// elem can be ID or actual DOM element object
		this.domElement = ZeroClipboard.$(elem);
		
		// float just above object, or zIndex 99 if dom element isn't set
		var zIndex = 99;
		if (this.domElement.style.zIndex) {
			zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
		}
		
		if (typeof(appendElem) == 'string') {
			appendElem = ZeroClipboard.$(appendElem);
		}
		else if (typeof(appendElem) == 'undefined') {
			appendElem = document.getElementsByTagName('body')[0];
		}
		
		// find X/Y position of domElement
		var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem);
		
		// create floating DIV above element
		this.div = document.createElement('div');
		var style = this.div.style;
		style.position = 'absolute';
		style.left = '' + box.left + 'px';
		style.top = '' + box.top + 'px';
		style.width = '' + box.width + 'px';
		style.height = '' + box.height + 'px';
		style.zIndex = zIndex;
		
		if (typeof(stylesToAdd) == 'object') {
			for (addedStyle in stylesToAdd) {
				style[addedStyle] = stylesToAdd[addedStyle];
			}
		}
		
		// style.backgroundColor = '#f00'; // debug
		
		appendElem.appendChild(this.div);
		
		this.div.innerHTML = this.getHTML( box.width, box.height );
	},
	
	getHTML: function(width, height) {
		// return HTML for movie
		var html = '';
		var flashvars = 'id=' + this.id + 
			'&width=' + width + 
			'&height=' + height;
			
		if (navigator.userAgent.match(/MSIE/)) {
			// IE gets an OBJECT tag
			var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
			html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
		}
		else {
			// all other browsers get an EMBED tag
			html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
		}
		return html;
	},
	
	hide: function() {
		// temporarily hide floater offscreen
		if (this.div) {
			this.div.style.left = '-2000px';
		}
	},
	
	show: function() {
		// show ourselves after a call to hide()
		this.reposition();
	},
	
	destroy: function() {
		// destroy control and floater
		if (this.domElement && this.div) {
			this.hide();
			this.div.innerHTML = '';
			
			var body = document.getElementsByTagName('body')[0];
			try { body.removeChild( this.div ); } catch(e) {;}
			
			this.domElement = null;
			this.div = null;
		}
	},
	
	reposition: function(elem) {
		// reposition our floating div, optionally to new container
		// warning: container CANNOT change size, only position
		if (elem) {
			this.domElement = ZeroClipboard.$(elem);
			if (!this.domElement) this.hide();
		}
		
		if (this.domElement && this.div) {
			var box = ZeroClipboard.getDOMObjectPosition(this.domElement);
			var style = this.div.style;
			style.left = '' + box.left + 'px';
			style.top = '' + box.top + 'px';
		}
	},
	
	setText: function(newText) {
		// set text to be copied to clipboard
		this.clipText = newText;
		if (this.ready) this.movie.setText(newText);
	},
	
	addEventListener: function(eventName, func) {
		// add user event listener for event
		// event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
		if (!this.handlers[eventName]) this.handlers[eventName] = [];
		this.handlers[eventName].push(func);
	},
	
	setHandCursor: function(enabled) {
		// enable hand cursor (true), or default arrow cursor (false)
		this.handCursorEnabled = enabled;
		if (this.ready) this.movie.setHandCursor(enabled);
	},
	
	setCSSEffects: function(enabled) {
		// enable or disable CSS effects on DOM container
		this.cssEffects = !!enabled;
	},
	
	receiveEvent: function(eventName, args) {
		// receive event from flash
		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
				
		// special behavior for certain events
		switch (eventName) {
			case 'load':
				// movie claims it is ready, but in IE this isn't always the case...
				// bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
				this.movie = document.getElementById(this.movieId);
				if (!this.movie) {
					var self = this;
					setTimeout( function() { self.receiveEvent('load', null); }, 1 );
					return;
				}
				
				// firefox on pc needs a "kick" in order to set these in certain cases
				if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
					var self = this;
					setTimeout( function() { self.receiveEvent('load', null); }, 100 );
					this.ready = true;
					return;
				}
				
				this.ready = true;
				this.movie.setText( this.clipText );
				this.movie.setHandCursor( this.handCursorEnabled );
				break;
			
			case 'mouseover':
				if (this.domElement && this.cssEffects) {
					this.domElement.addClass('hover');
					if (this.recoverActive) this.domElement.addClass('active');
				}
				break;
			
			case 'mouseout':
				if (this.domElement && this.cssEffects) {
					this.recoverActive = false;
					if (this.domElement.hasClass('active')) {
						this.domElement.removeClass('active');
						this.recoverActive = true;
					}
					this.domElement.removeClass('hover');
				}
				break;
			
			case 'mousedown':
				if (this.domElement && this.cssEffects) {
					this.domElement.addClass('active');
				}
				break;
			
			case 'mouseup':
				if (this.domElement && this.cssEffects) {
					this.domElement.removeClass('active');
					this.recoverActive = false;
				}
				break;
		} // switch eventName
		
		if (this.handlers[eventName]) {
			for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
				var func = this.handlers[eventName][idx];
			
				if (typeof(func) == 'function') {
					// actual function reference
					func(this, args);
				}
				else if ((typeof(func) == 'object') && (func.length == 2)) {
					// PHP style object + method, i.e. [myObject, 'myMethod']
					func[0][ func[1] ](this, args);
				}
				else if (typeof(func) == 'string') {
					// name of function
					window[func](this, args);
				}
			} // foreach event handler defined
		} // user defined handler for event
	}
	
};
